package com.strong.sample.table.t_user.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import com.strong.sample.table.t_unit.jpa.TableUnitDAO;
import com.strong.sample.table.t_user.jpa.TableUserDAO;
import com.strong.sample.table.t_user.jpa.TableUserDO;
import com.strong.sample.table.t_user.model.TableUserCreateDTO;
import com.strong.sample.table.t_user.model.TableUserRetrieveDTO;
import com.strong.sample.table.t_user.model.TableUserUpdateDTO;
import com.strong.sample.table.t_user.model.TableUserVO;
import com.strong.utils.JSON;
import com.strong.utils.StrongUtils;
import com.strong.utils.exception.StrongRuntimeException;
import com.strong.utils.jpa.service.StrongServiceImpl;
import com.strong.utils.message.StrongMessageSource;
import com.strong.utils.security.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.strong.sample.table.t_test.TableTestConstants.TABLE_ENTITY;
import static com.strong.sample.table.t_user.TableUserConstants.*;
import static com.strong.utils.constants.MessageConstants.*;
import static com.strong.utils.jpa.JpaConstants.*;
import static com.strong.utils.security.AuthorityConstants.STRS_AUTHORITY;

/**
 * 数据处理Service实现类
 *
 * @author Simen
 * @date 2022-02-07 20:11:23
 */
@Slf4j
@Service
@CacheConfig(cacheNames = {TABLE_ENTITY})
public class TableUserServiceImpl extends StrongServiceImpl<TableUserDO, TableUserVO> implements TableUserService {

    /**
     * 注入的消息处理类
     */
    private final StrongMessageSource messageSource;

    /**
     * 注入的DAO类
     */
    private final TableUserDAO tableUserDAO;

    /**
     * 注入的DAO类
     */
    private final TableUnitDAO tableUnitDAO;

    /**
     * 实例化
     *
     * @param messageSource 注入的消息处理类
     * @param tableUserDAO  注入的DAO类
     */
    @Autowired
    public TableUserServiceImpl(StrongMessageSource messageSource, TableUserDAO tableUserDAO, TableUnitDAO tableUnitDAO) {
        this.messageSource = messageSource;
        this.tableUserDAO = tableUserDAO;
        this.tableUnitDAO = tableUnitDAO;
    }

    @Override
    @Cacheable(key = "#root.target + '_' + #root.methodName + '_' + #intId")
    public TableUserDO getRecord(final Integer intId) {
        log.debug(intId.toString());
        Assert.notNull(intId, MSG_QUERY_ID_EMPTY);
        return tableUserDAO.getReferenceById(intId);
    }

    @Override
    @CacheEvict(allEntries = true)
    public TableUserDO getCreateAction(final TableUserCreateDTO modelCreate) {
        log.debug(JSON.toJSONString(modelCreate));
        Assert.notNull(modelCreate, MSG_CONTENT_EMPTY);

        // 校验模型
        verifyMode(modelCreate);

        TableUserDO entity = BeanUtil.toBean(modelCreate, TableUserDO.class, CopyOptions.create().setIgnoreNullValue(true));
        // 初始化实体类ID
        entity.setUserId(StrongUtils.getNoRepeatId(tableUserDAO::getCountById));
        // 签名密码
        entity.setUserLoginPassword(SecurityUtils.signBySalt(modelCreate.getUserLoginPassword(), modelCreate.getUserLoginSalt()));
        // 断言签名密码校验通过
        Assert.isTrue(SecurityUtils.verifyBySalt(modelCreate.getUserLoginPassword(), entity.getUserLoginPassword(), modelCreate.getUserLoginSalt()),
                "用户密码签名失败，请与管理员联系");

        tableUserDAO.save(entity);
        return entity;
    }

    @Override
    @Cacheable(keyGenerator = "vabKeyGenerator")
    public List<TableUserDO> getAllList(final TableUserRetrieveDTO modelRetrieve) {
        log.debug(JSON.toJSONString(modelRetrieve));
        Assert.notNull(modelRetrieve, MSG_SEARCH_CONDITION_EMPTY);

        // 以表达式生成一个查询条件对象
        Specification<TableUserDO> specification = getSpecification(modelRetrieve);
        return tableUserDAO.findAll(specification, modelRetrieve.getSort());
    }

    @Override
    @Cacheable(keyGenerator = "vabKeyGenerator")
    public Page<TableUserDO> getPageList(final TableUserRetrieveDTO modelRetrieve) {
        log.debug(JSON.toJSONString(modelRetrieve, true));
        Assert.notNull(modelRetrieve, MSG_SEARCH_CONDITION_EMPTY);

        // 以表达式生成一个查询条件对象
        Specification<TableUserDO> specification = getSpecification(modelRetrieve);
        return tableUserDAO.findAll(specification, modelRetrieve.getPageable());
    }

    @Override
    @CacheEvict(allEntries = true)
    public TableUserDO getUpdateAction(final TableUserUpdateDTO modelUpdate) {
        log.debug(JSON.toJSONString(modelUpdate));
        Assert.notNull(modelUpdate, MSG_CONTENT_EMPTY);

        // 通过id字段获取待修改的实体类（id字段不存在，需修改为对应字段）
        TableUserDO entity = tableUserDAO.getReferenceById(modelUpdate.getUserId());
        Assert.notNull(entity, MSG_RECORD_NONENTITY);
        BeanUtil.copyProperties(modelUpdate, entity, CopyOptions.create().setIgnoreNullValue(true));
        tableUserDAO.save(entity);
        return entity;
    }

    @Override
    @Cacheable(key = "#root.target + '_' + #root.methodName + '_' + #intId")
    public TableUserDO getUpdateRecord(final Integer intId) {
        log.debug(intId.toString());
        Assert.notNull(intId, MSG_QUERY_ID_EMPTY);
        return tableUserDAO.getReferenceById(intId);
    }

    @Override
    @CacheEvict(allEntries = true)
    public Integer[] getDeleteAction(final Integer... intsId) {
        log.debug(ArrayUtil.toString(intsId));
        Assert.notEmpty(intsId, MSG_DELETE_RECORD_EMPTY);

        int[] intsTemp = new int[intsId.length];
        for (int i = 0; i < intsId.length; i++) {
            Assert.notNull(intsId[i], MSG_DELETE_RECORD_EMPTY);
            intsTemp[i] = intsId[i];
        }

        Integer intCount = tableUserDAO.getCountByIdIn(intsTemp);
        Assert.isTrue(intCount == intsId.length, MSG_DELETE_RECORD_BEEN_NULL);

        tableUserDAO.deleteAllByIdInBatch(Arrays.asList(intsId));
        return intsId;
    }

    // ===================内部=======================

    /**
     * 获取查询范式
     *
     * @return {@link Specification}<{@link TableUserDO}>
     */
    protected Specification<TableUserDO> getSpecification(final TableUserRetrieveDTO modelRetrieve) {
        return (root, query, criteriaBuilder) -> {
//            Map<String, String> mapSearch = modelRetrieve.getMapSearch();
//            if (MapUtil.isNotEmpty(mapSearch)) {
//                List<Predicate> listPredicate = new ArrayList<>();
//
//                String strLike = MapUtil.getStr(mapSearch, TEST_STR_ENTITY);
//                if (StrUtil.isNotBlank(strLike)) {
//                    listPredicate.add(criteriaBuilder.like(root.get(TableUserDO_.testStr), strLike));
//                }
//
//                return criteriaBuilder.and(ArrayUtil.toArray(listPredicate, Predicate.class));
//            }
            return null;
        };
    }

    @Override
    @CacheEvict(value = TABLE_ENTITY, allEntries = true)
    public void cacheEvict() {

    }

    // ===================扩展=======================
    private void verifyMode(TableUserCreateDTO tableUserCreateDTO) {
        Map<String, String> mapError = new HashMap<>();

        StrongUtils.isTrue(tableUserDAO.getCountByLoginName(tableUserCreateDTO.getUserLoginName()) == 0,
                mapError, USER_LOGIN_NAME_ENTITY, ERROR_LOGIN_NAME_EXISTS);

        StrongUtils.isTrue(tableUnitDAO.getCountById(tableUserCreateDTO.getUserUnitId()) == 1,
                mapError, USER_UNIT_ID_ENTITY, ERROR_UNIT_ERROR);

        StrongUtils.isTrue(StrongUtils.isIdCode(tableUserCreateDTO.getUserIdNumber(), tableUserCreateDTO.getUserIdType()),
                mapError, USER_ID_NUMBER_ENTITY, ERROR_ID_NUMBER_NO_STANDARD);

        String[] strsAuthority = tableUserCreateDTO.getUserAuthority().split(",");
        StrongUtils.isTrue(ArrayUtil.containsAll(STRS_AUTHORITY, strsAuthority),
                mapError, USER_AUTHORITY_ENTITY, ERROR_AUTHORITY_FORMAT);

        if (MapUtil.isNotEmpty(mapError)) {
            throw new StrongRuntimeException(mapError);
        }
    }
}